package com.fastfly.domain;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.springframework.validation.Errors;

@Entity
public class Flight extends BaseEntity {
	
	public static final String SEARCH_BY_FLIGHT_NUMBER = "flightNumber";
	public static final String SEARCH_CITY = "city";

	public static final String FLIGHT_STATUS_N_A = "N/A";
	public static final String FLIGHT_STATUS_IN_FLIGHT = "In flight";
	public static final String FLIGHT_STATUS_AT_THE_GATE = "At the gate";
	public static final String FLIGHT_STATUS_DELAYED = "Delayed";
	public static final String FLIGHT_STATUS_ON_TIME = "On time";
	public static final String FLIGHT_STATUS_FINISHED = "Finished";

	private static final long serialVersionUID = 1L;

	

	@ManyToOne
	protected City departingCity;

	@ManyToOne
	protected City destinationCity;

	protected String flightNumber;

	@Temporal(value = TemporalType.TIMESTAMP)
	protected Date departureDate;

	protected int flightDurationInMinutes;
	protected int flightMiles;

	protected int numTotalSeats;
	
	protected double price;	
	
	@OneToMany (cascade=CascadeType.ALL, mappedBy="flight")
	protected List<Seat> seatCollection = new ArrayList<Seat>();
	
	
	// Query/Help Parameters for Forms ********************
	@Transient
	protected String departureTime;
	
	@Transient
	protected String searchBy = SEARCH_BY_FLIGHT_NUMBER;
	
	@Transient
	protected Date returnDate;	
	
	@Transient
	protected int numberPassengers = 1;
	//******************************************************


	public Flight() {
	}

	public Flight(City departingCity, City destinationCity, int seatAvailable) {
		this.departingCity = departingCity;
		this.destinationCity = destinationCity;
		numTotalSeats = seatAvailable;				
		for(int i = 1; i <= seatAvailable; i++) {
			Seat var = new Seat(this, i);			
			seatCollection.add(var);
		}
	}
	
	/* NOT WORKING
	@Where(clause="status = " + Seat.AVAILABLE_STATUS)
	@OrderBy(value="number")
	public List<Seat> getSeatAvailableCollection() {
		return seatCollection;
	}
	*/

	public String getDepartureTime() {
		String ret = departureTime;
		if (departureDate != null) {
			ret = getTimeFormat().format(departureDate);
		}
		return ret;

	}

	public void setDepartureTime(String departureTime) {
		this.departureTime = departureTime;
	}

	public void setupDepartureTime() throws ParseException {
		this.departureDate = setupDateTime(this.departureDate, this.departureTime);
	}

	public City getDepartingCity() {
		return departingCity;
	}
	
	

	public int getFlightMiles() {
		return flightMiles;
	}

	public void setFlightMiles(int flightMiles) {
		this.flightMiles = flightMiles;
	}

	public void setDepartingCity(City departingCity) {
		this.departingCity = departingCity;
	}

	public City getDestinationCity() {
		return destinationCity;
	}

	public void setDestinationCity(City destinationCity) {
		this.destinationCity = destinationCity;
	}

	public String getFlightNumber() {
		return flightNumber;
	}

	public void setFlightNumber(String flightNumber) {
		this.flightNumber = flightNumber;
	}

	public Date getDepartureDate() {
		return departureDate;
	}
	

	public double getPrice() {
		return price;
	}

	public void setPrice(double price) {
		this.price = price;
	}

	@Transient
	public String getFormattedDepartureDate() {
		return dateFormat.format(departureDate);
	}

	@Transient
	public String getFormattedArrivingDate() {
		Date var = getArrivingDate();
		return dateFormat.format(var);
	}

	@Transient
	public Date getArrivingDate() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(departureDate);
		cal.add(Calendar.MINUTE, flightDurationInMinutes);
		return cal.getTime();
	}

	@Transient
	public String getFormattedArrivingTime() {
		Date var = getArrivingDate();
		return getTimeFormat().format(var);
	}

	public void setDepartureDate(Date departureDate) {
		this.departureDate = departureDate;
	}

	public int getFlightDurationInMinutes() {
		return flightDurationInMinutes;
	}

	public void setFlightDurationInMinutes(int flightDurationInMinutes) {
		this.flightDurationInMinutes = flightDurationInMinutes;
	}

	
	public int getSeatAvailable() {
		int ret = 0;
		for(Seat seat: seatCollection) {
			if (seat.isAvailable()) {
				ret++;				
			}						
		}
		return ret;
	}

	
	public int getNumTotalSeats() {
		return numTotalSeats;
	}

	public void setNumTotalSeats(int numTotalSeats) {
		this.numTotalSeats = numTotalSeats;
	}

	public Date getReturnDate() {
		return returnDate;
	}

	public void setReturnDate(Date returnDate) {
		this.returnDate = returnDate;
	}

	public int getNumberPassengers() {
		return numberPassengers;
	}

	public void setNumberPassengers(int numberPassengers) {
		this.numberPassengers = numberPassengers;
	}

	public String getSearchBy() {
		return searchBy;
	}

	public void setSearchBy(String searchBy) {
		this.searchBy = searchBy;
	}

	@Transient
	public String getStatus() {
		String ret = FLIGHT_STATUS_FINISHED;
		
		Date now = new Date();
		
		Calendar calAnotherDay = Calendar.getInstance();		
		calAnotherDay.add(Calendar.DAY_OF_MONTH, 1);
		
		
		Calendar calOntime = Calendar.getInstance();		
		calOntime.add(Calendar.MINUTE, 32);
		
		
		Calendar calFinished = Calendar.getInstance();	
		calFinished.setTime(departureDate);
		calFinished.add(Calendar.MINUTE, flightDurationInMinutes + 32);

		
		if (departureDate.after(calAnotherDay.getTime())) {
			ret = FLIGHT_STATUS_N_A;
		} else if (departureDate.after(calOntime.getTime())  ) {			
			if (getRandomInt(10) == 1) {
				ret = FLIGHT_STATUS_DELAYED;
			} else {
				ret = FLIGHT_STATUS_ON_TIME;
			}			
		} else if (departureDate.after( now ) ) {
			ret = FLIGHT_STATUS_AT_THE_GATE;
		} else if (now.before( calFinished.getTime() ) ) {
			ret = FLIGHT_STATUS_IN_FLIGHT;
		}
		 

		return ret;
	}
	
	public void validateBookingSearchForm(Errors errors) {
		log.debug("validateBookingSearchForm: " + errors);
		validateDateHelper(departureDate, 1, "departureDate", errors);
		validateDateHelper(returnDate, 1, "returnDate", errors);
		if ( departureDate.after( returnDate ) ) {
			errors.rejectValue("returnDate", "", "Return date need to be after Departing date");
		}
		validateCities(errors);
	}	
	
	

	public void validateSearchFlight(Errors errors) {		
		log.debug("validateSearchFlight: " + errors);
		validateCities(errors);
		try {
			if (departureTime == null || !(departureTime.trim().length() > 0)) {
				departureTime = "00:00";
			}
			setupDepartureTime();
		} catch (ParseException e) {
			log.error("Invalid time: " + departureTime);
			errors.rejectValue("departureTime", "", "Invalid time!");
		}

		if (departureDate == null) {
			errors.rejectValue("departureDate", "", "Departure Date cannot be blank");
		} else if ( (new Date()).after(departureDate)) {
			errors.rejectValue("departureDate", "", "Departure Date need to be in the future");
		}
	}

	public void validateCities(Errors errors) {
		if (departingCity == null || destinationCity == null) {
			errors.rejectValue("destinationCity", "", "Departing City or Destination City cannot be null");
		} else if (departingCity.equals(destinationCity)) {
			errors.rejectValue("destinationCity", "", "Departing City and Destination City cannot be the same");
		}
	}

	public void validateDepartureDate(Errors errors) {
		validateDateHelper(departureDate, 0, "departureDate", errors);
	}

	/*
	protected void validateDateHelper(Date date, String dateName, Errors errors) {
		if (date == null) {
			errors.rejectValue(dateName, "", dateName + " cannot be blank");
		} else if (  (getDateWithSpecificTime(new Date(), 0, 0)).after(date)  ) {
			errors.rejectValue(dateName, "", dateName + " need to be in the future");
		}
	}
	*/
	
	protected void validateDateHelper(Date date, int daysToAdd, String dateName, Errors errors) {
		if (date == null) {
			errors.rejectValue(dateName, "", dateName + " cannot be blank");
		} else if (  (getDateWithSpecificTime(new Date(), daysToAdd, 0, 0)).after(date)  ) {
			errors.rejectValue(dateName, "", dateName + " need to be in the future");
		}
	}

}
